//
//  BindKey.swift
//  CooperSwift_Example
//
//  Created by 计海峰 on 2021/5/26.
//  Copyright © 2021 CocoaPods. All rights reserved.
//

import Foundation
import RxSwift

// MARK: - 动态绑定key
private struct AssociateKeys {
    static var bindKey: Void?
    static var parentKey: Void?
    static var targetKey: Void?
    static var targetKeys: Void?
    static var serviceHolderKey: Void?
}

// MARK: - 协议BindKey 用来 给一个固定的随机key 并有执行关联key对象的方法
public protocol BindKeyProtocol: AssociatedObjectStore { }


// MARK: - 执行或者发出方法
/// 执行方法必须 objc
/// 发出端必须addTargetKey
public extension BindKeyProtocol {
    
    // 执行方法
    @discardableResult
    func run(_ methodStr: String, info: Any? = nil) -> Bool{
        var methodKey = String(methodStr)
        if info != nil && methodKey.last != ":" {
            methodKey += ":"
        }
        if runPerform(self, method: methodKey, info: info) { return true }
        
        methodKey += info != nil ? "parent:" : ":"
        return runPerform(serviceHolder, method: methodKey, info: info, parent: self)
    }
    
    private func runPerform(_ target: NSObjectProtocol, method: String, info: Any? = nil, parent: Any? = nil) -> Bool {
        debugPrint("🍚🍚🍚 \(target) 执行方法：\(method) 信息：\(info ?? "")")
        let sel = Selector(method)
        if target.responds(to: sel)  {
            if info != nil {
                if parent != nil {
                    target.perform(sel, with: info, with: parent)
                }else {
                    target.perform(sel, with: info)
                }
            }else {
                if parent != nil {
                    target.perform(sel, with: parent)
                }else {
                    target.perform(sel)
                }
            }
            return true
        }
        return false
    }
    
    // MARK:- 发出方法 需要添加target key  ->  addTargetKey 绑定自身Key或prentKey或targetKey
    // 发出方法 接收方法 需要@objc
    func post(_ methodStr: String, info: Any? = nil, targetKey: String? = nil) {
        
        let methodKey = String(methodStr)
        
        // target key 不为空
        if let key = targetKey {
            sendMethod(key, methodStr: methodKey, info: info)
            return
        }
        
        // 默认target key
        if let key = self.targetKey {
            sendMethod(key, methodStr: methodKey, info: info)
        }
        
        if self.targetKeys.count > 0 {
            self.targetKeys.forEach { key in
                sendMethod(key, methodStr: methodKey, info: info)
            }
        }
        
    }
    
    func sendMethod(_ targetKey: String, methodStr: String, info: Any? = nil) {
        if targetKey.contains("Observer") {
            NotificationCenter.default.post(name: NSNotification.Name.init(targetKey), object: (methodStr, info))
        }else {
            debugPrint("not found add notification:  \(targetKey)")
        }
    }
    
}

// MARK: - BindKey TableCellConfig 扩展
public extension BindKeyProtocol where Self: TableCellConfigProtocol {
    // MARK: - 发出方法 需要添加target key  ->  addTargetKey 绑定自身Key或prentKey或targetKey
    // 发出方法 重写方法 需要@objc
    func postTableService(_ service: TableViewModelRunServiceEnum, info: Any? = nil, targetKey: String? = nil) {
        let info = info ?? self.bindKey()
        post(service.runMethod(), info: info, targetKey: targetKey)
    }
}


// MARK: - BindKey TableCellConfig 扩展
public extension BindKeyProtocol where Self: CollectionCellConfigProtocol {
    // MARK: - 发出方法 需要添加target key  ->  addTargetKey 绑定自身Key或prentKey或targetKey
    // 发出方法 重写方法 需要@objc
    func postCollectionService(_ service: CollectionViewModelRunServiceEnum, info: Any? = nil, targetKey: String? = nil) {
        if let targetInfo = info, let attach = service.runAttachInfo() { /// 需要提交 执行方法
            post(service.runMethod(), info: (targetInfo, attach), targetKey: targetKey)
            return
        }
        let info = info ?? self.bindKey()
        post(service.runMethod(), info: info, targetKey: targetKey)
    }
}

// MARK: - key值绑定&初始化
public extension BindKeyProtocol {
    
    // 生成key 方法
    func genKeyRandom() -> String {
        return String(describing: self) + "_\(Date().timeString())_\(Int(arc4random()))"
    }

    // MARK: - selfKey
    private var selfKey: String? {
         get {
            return objc_getAssociatedObject(self, &AssociateKeys.bindKey) as? String
        }
        set {
            objc_setAssociatedObject(self, &AssociateKeys.bindKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
        }
    }
    
    // MARK: - parentKey
    var parentKey: String? {
         get {
            return objc_getAssociatedObject(self, &AssociateKeys.parentKey) as? String
        }
        set {
            objc_setAssociatedObject(self, &AssociateKeys.parentKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
        }
    }
    
    // MARK: - 生成一个key给selfKey 给自身用selfKey作为标识添加监听
    @discardableResult
    func bindKey(_ bag: DisposeBag? = nil) -> String {
        if let key = self.selfKey {
            return key
        }
        let key = genKeyRandom() + (bag != nil ? "_Observer" : "")
        self.selfKey = key
        
        guard let disposeBag = bag else { return key }
        
        //MARK: - 需要绑定监听  接手通知
        NotificationCenter.default.rx
            .notification(NSNotification.Name(key))
//            .take(until: deallocated)
            .subscribe(onNext: { [weak self] notification in
            guard let data = notification.object as? (String, Any?) else {
                return
            }
            self?.run(data.0, info: data.1)
        }).disposed(by: disposeBag)
        return key
    }
    
    
    //MARK: - 添加自身可执行的目标targetkey
    @discardableResult
    func addTargetKey(_ targetKey: String?, isTarget: Bool = true) -> Self {
        guard let key = targetKey else {
            return self
        }
        if key.clearText().isEmpty {
            return self
        }
        if isTarget {
            self.targetKey = key
        }else {
            self.targetKeys.insert(key)
        }
        return self
    }
    
    // MARK: - target key
    var targetKey: String? {
        get {
            return objc_getAssociatedObject(self, &AssociateKeys.targetKey) as? String
        }
        set {
            objc_setAssociatedObject(self, &AssociateKeys.targetKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
        }
    }
    
    /// 多个target
    var targetKeys: Set<String> {
        get {
            return associatedObject(forKey: &AssociateKeys.targetKeys, default: [], ploicy: .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        set {
            setAssociatedObject(newValue, forKey: &AssociateKeys.targetKeys, ploicy: .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

}

// MARK: - BindKey 扩展 serviceHolder 属性 作为 服务转接
public extension BindKeyProtocol {
    
    var serviceHolder: ServiceHolder {
        get {
            return associatedObject(forKey: &AssociateKeys.serviceHolderKey, default: ServiceHolder(), ploicy: .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        set {
            setAssociatedObject(newValue, forKey: &AssociateKeys.serviceHolderKey, ploicy: .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
}
